package com.ioc.annotation;

import com.ioc.utils.ClassUtil;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author xinwang
 * @date 2020/2/27 18:15
 * 手写 springIoc 注解版
 **/
public class ExtClassAnnotationApplicationContext {
    //扫包的范围
    private String packageName;
    //容器存放对象
    private ConcurrentHashMap<String, Object> beans = null;

    public ExtClassAnnotationApplicationContext(String packageName) throws Exception {
        beans = new ConcurrentHashMap();
        this.packageName = packageName;
        initBeans();
        initEntryField();
    }

    /**
     * 初始化属性
     * @throws IllegalAccessException
     */
    public void initEntryField() throws IllegalAccessException {
        //1.遍历所有的bean容器对象
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //2.判断属性上面是否有加注解
            Object value = entry.getValue();
            dependInject(value);
        }
    }
    /**
     * 从容器中拿到对应的bean对象
     *
     * @param beanId 传入的beanId
     * @return
     * @throws Exception
     */
    public Object getBean(String beanId) throws Exception {
        if (StringUtils.isEmpty(beanId)) {
            throw new Exception("beanId参数不能为空!");
        }
        Object obj = beans.get(beanId);
        return obj;
    }

    /**
     * 初始化对象
     */
    public void initBeans() throws Exception {
        //1.使用反射机制扫包，获取当前包下所有的类
        List<Class<?>> classes = ClassUtil.getClasses(packageName);
        //2.判断类是否有注解
        ConcurrentHashMap<String, Object> classExtAnnotation = findClassExtAnnotation(classes);
        if (classExtAnnotation == null || classExtAnnotation.isEmpty()) {
            //测试用
            throw new Exception("该包下没有任何类加上注解");
        }
    }

    /**
     * 判断类上是否有注解
     *
     * @param classes 获取到的所有类class
     */
    public ConcurrentHashMap<String, Object> findClassExtAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException {
        for (Class<?> classInfo : classes) {
            ExtService annotation = classInfo.getAnnotation(ExtService.class);
            if (annotation != null) {
                //获取当前类名 首字母小写
                String beanId = toLowerCaseFirstOne(classInfo.getSimpleName());
                //拿到class地址创建对象
                Object obj = newInstance(classInfo);
                //将创建带有注解的对象 存入容器中
                beans.put(beanId, obj);
            }
        }
        return beans;
    }


    /**
     * 拿到class地址 反射创建对象
     *
     * @param classInfo
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public Object newInstance(Class<?> classInfo) throws IllegalAccessException, InstantiationException {
        return classInfo.newInstance();
    }

    /**
     * 首字母转换小写
     *
     * @param s
     * @return
     */
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return (new StringBuffer()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
        }
    }

    /**
     * DI 依赖注入
     * @param object
     * @throws IllegalAccessException
     */
    public void dependInject(Object object) throws IllegalAccessException {
        //1.使用反射机制，获取到当前类的所有属性
        Field[] declaredFields = object.getClass().getDeclaredFields();
        //2.判断当前类是否存在注解
        for (Field declaredField : declaredFields) {
            ExtAutowired annotation = declaredField.getAnnotation(ExtAutowired.class);
            if (annotation !=null){
                String beanId = declaredField.getName();
                Object bean = beans.get(beanId);
                if (bean!=null){
                    //允许访问私有的
                    declaredField.setAccessible(true);
                    declaredField.set(object,bean);
                }
            }
        }
    }
}
